/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.io.rest.core.thing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.eclipse.smarthome.config.core.ConfigDescription;
import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry;
import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTO;
import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTOMapper;
import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterDTO;
import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterGroupDTO;
import org.eclipse.smarthome.core.auth.Role;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.binding.firmware.Firmware;
import org.eclipse.smarthome.core.thing.dto.ChannelDefinitionDTO;
import org.eclipse.smarthome.core.thing.dto.ChannelGroupDefinitionDTO;
import org.eclipse.smarthome.core.thing.dto.StrippedThingTypeDTO;
import org.eclipse.smarthome.core.thing.dto.StrippedThingTypeDTOMapper;
import org.eclipse.smarthome.core.thing.dto.ThingTypeDTO;
import org.eclipse.smarthome.core.thing.firmware.FirmwareRegistry;
import org.eclipse.smarthome.core.thing.firmware.dto.FirmwareDTO;
import org.eclipse.smarthome.core.thing.type.BridgeType;
import org.eclipse.smarthome.core.thing.type.ChannelDefinition;
import org.eclipse.smarthome.core.thing.type.ChannelGroupDefinition;
import org.eclipse.smarthome.core.thing.type.ChannelGroupType;
import org.eclipse.smarthome.core.thing.type.ChannelType;
import org.eclipse.smarthome.core.thing.type.ThingType;
import org.eclipse.smarthome.core.thing.type.ThingTypeRegistry;
import org.eclipse.smarthome.core.thing.type.TypeResolver;
import org.eclipse.smarthome.io.rest.LocaleUtil;
import org.eclipse.smarthome.io.rest.SatisfiableRESTResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
/**
* ThingTypeResource provides access to ThingType via REST.
*
* @author Dennis Nobel - Initial contribution
* @author Kai Kreuzer - refactored for using the OSGi JAX-RS connector
* @author Thomas Höfer - Added thing and thing type properties
* @author Chris Jackson - Added parameter groups, advanced, multipleLimit,
* limitToOptions
* @author Yordan Zhelev - Added Swagger annotations
* @author Miki Jankov - Introducing StrippedThingTypeDTO
* @author Franck Dechavanne - Added DTOs to ApiResponses
*/
@Path(ThingTypeResource.PATH_THINGS_TYPES)
@Api(value = ThingTypeResource.PATH_THINGS_TYPES)
public class ThingTypeResource implements SatisfiableRESTResource {
/** The URI path to this resource */
public static final String PATH_THINGS_TYPES = "thing-types";
private final Logger logger = LoggerFactory.getLogger(ThingTypeResource.class);
private ThingTypeRegistry thingTypeRegistry;
private ConfigDescriptionRegistry configDescriptionRegistry;
private FirmwareRegistry firmwareRegistry;
protected void setThingTypeRegistry(ThingTypeRegistry thingTypeRegistry) {
this.thingTypeRegistry = thingTypeRegistry;
}
protected void unsetThingTypeRegistry(ThingTypeRegistry thingTypeRegistry) {
this.thingTypeRegistry = null;
}
protected void setConfigDescriptionRegistry(ConfigDescriptionRegistry configDescriptionRegistry) {
this.configDescriptionRegistry = configDescriptionRegistry;
}
protected void unsetConfigDescriptionRegistry(ConfigDescriptionRegistry configDescriptionRegistry) {
this.configDescriptionRegistry = null;
}
protected void setFirmwareRegistry(FirmwareRegistry firmwareRegistry) {
this.firmwareRegistry = firmwareRegistry;
}
protected void unsetFirmwareRegistry(FirmwareRegistry firmwareRegistry) {
this.firmwareRegistry = null;
}
@GET
@RolesAllowed({ Role.USER })
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets all available thing types without config description, channels and properties.", response = StrippedThingTypeDTO.class, responseContainer = "Set")
@ApiResponses(value = @ApiResponse(code = 200, message = "OK", response = StrippedThingTypeDTO.class, responseContainer = "Set"))
public Response getAll(
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @ApiParam(value = HttpHeaders.ACCEPT_LANGUAGE) String language) {
Locale locale = LocaleUtil.getLocale(language);
Set<StrippedThingTypeDTO> strippedThingTypeDTOs = convertToStrippedThingTypeDTOs(
thingTypeRegistry.getThingTypes(locale), locale);
return Response.ok(strippedThingTypeDTOs).build();
}
@GET
@RolesAllowed({ Role.USER })
@Path("/{thingTypeUID}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets thing type by UID.", response = ThingTypeDTO.class)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Thing type with provided thingTypeUID does not exist.", response = ThingTypeDTO.class),
@ApiResponse(code = 404, message = "No content") })
public Response getByUID(@PathParam("thingTypeUID") @ApiParam(value = "thingTypeUID") String thingTypeUID,
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @ApiParam(value = HttpHeaders.ACCEPT_LANGUAGE) String language) {
Locale locale = LocaleUtil.getLocale(language);
ThingType thingType = thingTypeRegistry.getThingType(new ThingTypeUID(thingTypeUID), locale);
if (thingType != null) {
return Response.ok(convertToThingTypeDTO(thingType, locale)).build();
} else {
return Response.noContent().build();
}
}
@GET
@Path("/{thingTypeUID}/firmwares")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Get all available firmwares for provided thingType", response = StrippedThingTypeDTO.class, responseContainer = "Set")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 204, message = "No firmwares found.") })
public Response getFirmwares(@PathParam("thingTypeUID") @ApiParam(value = "thingTypeUID") String thingTypeUID,
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @ApiParam(value = HttpHeaders.ACCEPT_LANGUAGE) String language) {
ThingTypeUID athingTypeUID = new ThingTypeUID(thingTypeUID);
Collection<Firmware> firmwares = firmwareRegistry.getFirmwares(athingTypeUID, LocaleUtil.getLocale(language));
List<FirmwareDTO> firmwareList = convertToFirmwareDTO(firmwares);
if (firmwareList.isEmpty()) {
return Response.status(Status.NO_CONTENT).build();
}
return Response.ok().entity(firmwareList).build();
}
private ThingTypeDTO convertToThingTypeDTO(ThingType thingType, Locale locale) {
final ConfigDescription configDescription;
if (thingType.getConfigDescriptionURI() != null) {
configDescription = this.configDescriptionRegistry.getConfigDescription(thingType.getConfigDescriptionURI(),
locale);
} else {
configDescription = null;
}
List<ConfigDescriptionParameterDTO> parameters;
List<ConfigDescriptionParameterGroupDTO> parameterGroups;
if (configDescription != null) {
ConfigDescriptionDTO configDescriptionDTO = ConfigDescriptionDTOMapper.map(configDescription);
parameters = configDescriptionDTO.parameters;
parameterGroups = configDescriptionDTO.parameterGroups;
} else {
parameters = new ArrayList<>(0);
parameterGroups = new ArrayList<>(0);
}
final List<ChannelDefinitionDTO> channelDefinitions = convertToChannelDefinitionDTOs(
thingType.getChannelDefinitions(), locale);
if (channelDefinitions == null) {
return null;
}
return new ThingTypeDTO(thingType.getUID().toString(), thingType.getLabel(), thingType.getDescription(),
thingType.isListed(), parameters, channelDefinitions,
convertToChannelGroupDefinitionDTOs(thingType.getChannelGroupDefinitions(), locale),
thingType.getSupportedBridgeTypeUIDs(), thingType.getProperties(), thingType instanceof BridgeType,
parameterGroups);
}
private List<ChannelGroupDefinitionDTO> convertToChannelGroupDefinitionDTOs(
List<ChannelGroupDefinition> channelGroupDefinitions, Locale locale) {
List<ChannelGroupDefinitionDTO> channelGroupDefinitionDTOs = new ArrayList<>();
for (ChannelGroupDefinition channelGroupDefinition : channelGroupDefinitions) {
String id = channelGroupDefinition.getId();
ChannelGroupType channelGroupType = TypeResolver.resolve(channelGroupDefinition.getTypeUID(), locale);
// Default to the channelGroupDefinition label to override the
// channelGroupType
String label = channelGroupDefinition.getLabel();
if (label == null) {
label = channelGroupType.getLabel();
}
// Default to the channelGroupDefinition description to override the
// channelGroupType
String description = channelGroupDefinition.getDescription();
if (description == null) {
description = channelGroupType.getDescription();
}
List<ChannelDefinition> channelDefinitions = channelGroupType.getChannelDefinitions();
List<ChannelDefinitionDTO> channelDefinitionDTOs = convertToChannelDefinitionDTOs(channelDefinitions,
locale);
channelGroupDefinitionDTOs
.add(new ChannelGroupDefinitionDTO(id, label, description, channelDefinitionDTOs));
}
return channelGroupDefinitionDTOs;
}
private List<ChannelDefinitionDTO> convertToChannelDefinitionDTOs(List<ChannelDefinition> channelDefinitions,
Locale locale) {
List<ChannelDefinitionDTO> channelDefinitionDTOs = new ArrayList<>();
for (ChannelDefinition channelDefinition : channelDefinitions) {
ChannelType channelType = TypeResolver.resolve(channelDefinition.getChannelTypeUID(), locale);
if (channelType == null) {
logger.warn("Cannot find channel type: {}", channelDefinition.getChannelTypeUID());
return null;
}
// Default to the channelDefinition label to override the
// channelType
String label = channelDefinition.getLabel();
if (label == null) {
label = channelType.getLabel();
}
// Default to the channelDefinition description to override the
// channelType
String description = channelDefinition.getDescription();
if (description == null) {
description = channelType.getDescription();
}
ChannelDefinitionDTO channelDefinitionDTO = new ChannelDefinitionDTO(channelDefinition.getId(),
channelDefinition.getChannelTypeUID().toString(), label, description, channelType.getTags(),
channelType.getCategory(), channelType.getState(), channelType.isAdvanced(),
channelDefinition.getProperties());
channelDefinitionDTOs.add(channelDefinitionDTO);
}
return channelDefinitionDTOs;
}
private Set<StrippedThingTypeDTO> convertToStrippedThingTypeDTOs(List<ThingType> thingTypes, Locale locale) {
Set<StrippedThingTypeDTO> strippedThingTypeDTOs = new HashSet<>();
for (ThingType thingType : thingTypes) {
final StrippedThingTypeDTO strippedThingTypeDTO = StrippedThingTypeDTOMapper.map(thingType, locale);
if (strippedThingTypeDTO != null) {
strippedThingTypeDTOs.add(strippedThingTypeDTO);
} else {
logger.warn("Cannot create DTO for thingType '{}'. Skip it.", thingType);
}
}
return strippedThingTypeDTOs;
}
private List<FirmwareDTO> convertToFirmwareDTO(Collection<Firmware> firmwares) {
List<FirmwareDTO> firmwareList = new ArrayList<>();
for (Firmware firmware : firmwares) {
firmwareList.add(new FirmwareDTO(firmware.getUID().toString(), firmware.getVendor(), firmware.getModel(),
firmware.getDescription(), firmware.getVersion(), firmware.getPrerequisiteVersion(),
firmware.getChangelog()));
}
return firmwareList;
}
@Override
public boolean isSatisfied() {
return thingTypeRegistry != null && configDescriptionRegistry != null && firmwareRegistry != null;
}
}